﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using MyVocabulary.App_Code;

namespace MyVocabulary
{
    public partial class AbbVerbConjugation : Form
    {
        #region Consts

        protected static class FormMessages
        {
            public const string EmptyPersonString = "The person value can not be empty";
            public const string ValidateTense = "Please enter a value for the verb's tense";
        }

        public enum AbbVerbConjugationMode
        {
            Add,
            Edit
        }

        #endregion

        #region Fields & Properties

        /// <summary>
        /// A list of existing tenses that will be used as a suggestion in the cbTense combobox
        /// </summary>
        public List<string> ExistingTenses { get; set; }

        protected AbbVerbConjugationMode mode;
        /// <summary>
        /// Indicate if the form is in the add or edit mode.
        /// </summary>
        public AbbVerbConjugationMode Mode
        {
            get{ return mode;} 
            set
            {
                mode = value;
                Text = mode.ToString(); //Set the form title
            }
        }

        /// <summary>
        /// The list of grammatical persons to use by default when the conjugation is empty
        /// </summary>
        public List<Person> Persons { get; set; }

        /// <summary>
        /// This object contains all the data that appears on the form.
        /// When the user presses the ok button all the values entered in the form are saved in this object.
        /// </summary>
        public VerbConjugation VerbConjugation { get; set; }

        /// <summary>
        /// Occurs after the user presses the 'OK' button and the data is saved into the VerbConjugation property.
        /// </summary>
        public event EventHandler OnDataSaved;

        /// <summary>
        /// True if the conjugations DataGridView contains invalid data
        /// </summary>
        protected bool dataGridViewHasErrors;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new form
        /// </summary>
        /// <param name="Mode">Indicate if the form is in the add or edit mode</param>
        /// <param name="OnDataSaved">Occurs after the user presses the 'OK' button and the data is saved into the VerbConjugation property</param>
        /// <param name="Persons">The list of grammatical persons to use by default when the conjugation is empty</param>
        /// <param name="ExistingTenses">A list of existing tenses that will be used as a suggestion in the cbTense combobox</param>
        public AbbVerbConjugation(AbbVerbConjugationMode Mode, EventHandler OnDataSaved, List<Person> Persons, List<string> ExistingTenses)
        {
            InitializeComponent();
           
            this.Mode = Mode;           
            this.OnDataSaved = OnDataSaved;
            this.Persons = Persons;
            this.ExistingTenses = ExistingTenses;
            VerbConjugation = new VerbConjugation {Conjugation = new List<KeyValuePair<string, string>>()};

            SetTenseItems();
            SetDataGrid(); 
        }

        /// <summary>
        /// Create a new form
        /// </summary>
        /// <param name="Mode">Indicate if the form is in the add or edit mode</param>
        /// <param name="OnDataSaved">Occurs after the user presses the 'OK' button and the data is saved into the VerbConjugation property</param>
        /// <param name="Persons">The list of grammatical persons to use by default when the conjugation is empty</param>
        /// <param name="ExistingTenses">A list of existing tenses that will be used as a suggestion in the cbTense combobox</param>
        ///  <param name="VerbConjugation">This object contains all the data that appears on the form. 
        /// When the user presses the ok button all the values entered in the form are saved in this object.</param>
        public AbbVerbConjugation(AbbVerbConjugationMode Mode, EventHandler OnDataSaved, List<Person> Persons, List<string> ExistingTenses, VerbConjugation VerbConjugation)
        {
            InitializeComponent();

            this.Mode = Mode;            
            this.OnDataSaved = OnDataSaved;
            this.Persons = Persons;
            this.ExistingTenses = ExistingTenses;
            this.VerbConjugation = VerbConjugation;

            cbTense.Text = VerbConjugation.Tense;
            SetText(tbExamples, VerbConjugation.Examples);
            SetText(tbRemarks, VerbConjugation.Remarks);

            SetTenseItems();
            SetDataGrid();
        }

        #endregion

        #region DataGridView methods

        /// <summary>
        /// Set the data for the conjugation DataGridView
        /// </summary>
        protected void SetDataGrid()
        {
            var dt = new DataTable();
            var dc = dt.Columns.Add("Person");
            dc.AllowDBNull = false;
            dt.Columns.Add("Conjugation");

            if (VerbConjugation != null && VerbConjugation.Conjugation != null && VerbConjugation.Conjugation.Count > 0)
            {
                foreach (var item in VerbConjugation.Conjugation)
                {
                    var dr = dt.NewRow();
                    dr["Person"] = item.Key;
                    dr["Conjugation"] = item.Value;
                    dt.Rows.Add(dr);
                }
            }
            else if (Persons != null)//Add only the persons
            {
                foreach (var item in Persons)
                {
                    var dr = dt.NewRow();
                    dr["Person"] = item.Text;                    
                    dt.Rows.Add(dr);                    
                }
            }

            dgConjugation.DataSource = dt;                   
        }

        /// <summary>
        /// Validate that the "Person" column does not allow empty values
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgConjugation_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {           
            var headerText = dgConjugation.Columns[e.ColumnIndex].HeaderText;
            if (!headerText.Equals("Person")) 
                return;

            if (!string.IsNullOrEmpty(e.FormattedValue.ToString().Trim())) 
                return;

            dgConjugation.Rows[e.RowIndex].ErrorText = FormMessages.EmptyPersonString;
            if (dgConjugation.RowHeadersWidth < 50) //Make sure that the alert immage appears on the row header
                dgConjugation.RowHeadersWidth = 50;

            errorProvider1.SetError(dgConjugation, FormMessages.EmptyPersonString);
            dataGridViewHasErrors = true;

            e.Cancel = true;
        }

        /// <summary>
        /// Clear the row error in case the user presses ESC.  
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgConjugation_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {             
            dgConjugation.Rows[e.RowIndex].ErrorText = string.Empty;
            errorProvider1.SetError(dgConjugation, string.Empty);
            dataGridViewHasErrors = false;
        }

        /// <summary>
        /// Enable the use of the ESC key to exit from edit mode (not to close the form)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgConjugation_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
        {
            e.Control.PreviewKeyDown -= OnPreviewKeyDown;
            e.Control.PreviewKeyDown += OnPreviewKeyDown;
        }

        /// <summary>
        /// Exit from edit mode when the ESC key is pressed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Escape)
            {
                dgConjugation.CancelEdit();
                dgConjugation.EndEdit();
            }
        }

        #endregion

        #region TextBoxes

        /// <summary>
        /// Fix multiline TextBoxes that do not show new lines
        /// </summary>
        /// <param name="tb"></param>
        /// <param name="text"></param>
        protected static void SetText(TextBox tb, string text)
        {
            if (tb.Multiline)
            {
                tb.Lines = (new Regex(@"\n|\r\n").Split(text));
            }
            else
            {
                tb.Text = text;
            }
        }

        /// <summary>
        /// Set existing tenses suggestions
        /// </summary>
        protected void SetTenseItems()
        {
            cbTense.Items.Clear();
            cbTense.Items.AddRange(ExistingTenses.ToArray());
        }

        /// <summary>
        /// The verb tense is mandatory
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbTense_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (string.IsNullOrEmpty(cbTense.Text.Trim()))
                errorProvider1.SetError(cbTense, FormMessages.ValidateTense);
            else
                errorProvider1.SetError(cbTense, string.Empty);

        }

        /// <summary>
        /// Enable ctrl + A in TextBoxes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (!e.Control || e.KeyCode != Keys.A || !(sender is TextBox))
                return;

            var txt = (TextBox)sender;
            txt.SelectAll();
            e.Handled = true;
        }

        #endregion
     
        #region Form Shortcuts & Buttons

        /// <summary>
        /// Enable form shortcuts:
        /// * Ctrl + S - Save the form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AbbVerbConjugation_KeyDown(object sender, KeyEventArgs e)
        {
            if (!e.Control) return;

            switch (e.KeyCode)
            {
                case Keys.S:
                    e.Handled = true;
                    e.SuppressKeyPress = true;
                    btnOK_Click(sender, e);
                    break;
            }
        }

        /// <summary>
        /// Save the form data into the VerbConjugation property and call the OnDataSaved event handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnOK_Click(object sender, EventArgs e)
        {
            if (dataGridViewHasErrors || string.IsNullOrEmpty(cbTense.Text.Trim()))
                return;

            VerbConjugation.Tense = cbTense.Text;
            VerbConjugation.Examples = tbExamples.Text;
            VerbConjugation.Remarks = tbRemarks.Text;

            VerbConjugation.Conjugation.Clear();
            foreach (DataRow dr in ((DataTable)dgConjugation.DataSource).Rows)
            {
                VerbConjugation.Conjugation.Add(new KeyValuePair<string, string>((string)dr["Person"],
                                                                                 dr["Conjugation"] == DBNull.Value
                                                                                     ? string.Empty
                                                                                     : (string)dr["Conjugation"]));
            }

            if (OnDataSaved != null)
                OnDataSaved(this, null);

            Close();
        }

        /// <summary>
        /// Close the form
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        #endregion
    }
}
